package socks5

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"strconv"
)

var propSocks5 PropSocks5

func InitSer(varSocks5 PropSocks5) bool {
	if varSocks5.Port < 5000 {
		varSocks5.Port = 20000
	}
	if varSocks5.MaxCoroutine < 100 {
		varSocks5.MaxCoroutine = 100
	}
	propSocks5 = varSocks5
	//fmt.Printf("init varSocks5 : %v \n", propSocks5)
	return true
}

// 开启监听
func Listen() (bool, net.Listener) {
	server, err := net.Listen("tcp", ":"+strconv.Itoa(propSocks5.Port))
	if err != nil {
		return false, server
	}
	return true, server
}

// 获取原客户端情况
func GetClient(server net.Listener) (bool, net.Conn) {
	client, err := server.Accept()
	if err != nil {
		//fmt.Printf("Accept failed: %v", err)
		return false, client
	}
	return true, client
}

// 获得与原来的客户端和目标服务信息
func Socks5Connect(client net.Conn) (net.Conn, error) {
	buf := make([]byte, 256)

	//
	n, err := io.ReadFull(client, buf[:4])
	if n != 4 {
		return nil, errors.New("read header: " + err.Error())
	}

	ver, cmd, _, atyp := buf[0], buf[1], buf[2], buf[3]
	if ver != 5 || cmd != 1 {
		return nil, errors.New("invalid ver/cmd")
	}

	addr := ""
	switch atyp {
	case 1:
		n, err = io.ReadFull(client, buf[:4])
		if n != 4 {
			return nil, errors.New("invalid IPv4: " + err.Error())
		}
		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])

	case 3:
		n, err = io.ReadFull(client, buf[:1])
		if n != 1 {
			return nil, errors.New("invalid hostname: " + err.Error())
		}
		addrLen := int(buf[0])

		n, err = io.ReadFull(client, buf[:addrLen])
		if n != addrLen {
			return nil, errors.New("invalid hostname: " + err.Error())
		}
		addr = string(buf[:addrLen])

	case 4:
		return nil, errors.New("IPv6: no supported yet")

	default:
		return nil, errors.New("invalid atyp")
	}

	n, err = io.ReadFull(client, buf[:2])
	if n != 2 {
		return nil, errors.New("read port: " + err.Error())
	}
	port := binary.BigEndian.Uint16(buf[:2])

	destAddrPort := fmt.Sprintf("%s:%d", addr, port)
	dest, err := net.Dial("tcp", destAddrPort)
	if err != nil {
		return nil, errors.New("dial dst: " + err.Error())

	}

	n, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		dest.Close()
		return nil, errors.New("write rsp: " + err.Error())
	}
	return dest, nil
}

// 转发请求
func Socks5Forward(client, target net.Conn) {
	forward := func(src net.Conn, dest net.Conn) {
		defer src.Close()
		defer dest.Close()
		io.Copy(src, dest)

	}
	go forward(client, target)
	go forward(target, client)
}
